/*                          UX-Framework                               
 *                                                                 
 *  iZE Co.,Ltd. Copyright 2009 All rights reserved.          
 *  This file is distributed under the terms of the GNU Lesser 
 *  General Public License, described in file LICENSE.                                     
 */
package jp.ize.ux.components;

// {{{ imports
import jp.ize.ux.UXObject;
import jp.ize.ux.states.UXBasicState;
import jp.ize.ux.UXController;
import jp.ize.ux.UXInteraction;
import jp.ize.ux.geom.UXSize;
import jp.ize.ux.acts.Act;
import jp.ize.ux.components.UXScrollBar;
import jp.ize.ux.events.UXMouseEvent;
import flash.display.DisplayObjectContainer;
import flash.events.Event;
// }}}
// {{{ enum ScrollPolicy
enum ScrollPolicy
{
	/** Area内のコンポーネントがサイズを越えた場合にスクロールバーを表示します。 */
	AUTO;
	/** 常にスクロールバーを表示します。*/
	ON;
	/** 常にスクロールバーを表示しません。*/
	OFF;
}
// }}}
// {{{ class UXScrollAreaState extends UXBasicState
/**
 * UXScrollAreaState クラス<br>
 * UXScrollAreaの現在の状態を表すオブジェクトです。
 */
class UXScrollAreaState extends UXBasicState
{
	// {{{ public var hScrollBar( default, default ) : UXScrollBar;
	/**
	 * 水平スクロールバーの参照を取得します。
	 */
	public var hScrollBar( default, null ) : UXScrollBar;
	// }}}
	// {{{ public var hScrollPolicy( default, default ) : ScrollPolicy;
	/**
	 * 水平スクロールバーのポリシーを示す値を取得または設定します。
	 */
	public var hScrollPolicy( default, default ) : ScrollPolicy;
	// }}}
	// {{{ public var vScrollBar( default, default ) : UXScrollBar;
	/**
	 * 垂直スクロールバーの参照を取得します。
	 */
	public var vScrollBar( default, null ) : UXScrollBar;
	// }}}
	// {{{ public var vScrollPolicy( default, default ) : ScrollPolicy;
	/**
	 * 垂直スクロールバーのポリシーを示す値を取得または設定します。
	 */
	public var vScrollPolicy( default, default ) : ScrollPolicy;
	// }}}
	// {{{ public var auto(default, default) : Bool
	/**
	 * 子要素の動きに従ってスクロールバーを出現・消失・伸縮させる動作を自動的に行うかどうかのフラグ。デフォルトはtrueです。
	 */
	public var auto(default, default) : Bool;
	// }}}
	// {{{ public var size(default, default) : UXSize
	/**
	 * エリアの幅・高さです。
	 */
	public var size(default, default) : UXSize;
	// }}}
	// {{{ public function new( size:UXSize, ?hScrollPolicy:ScrollPolicy, ?vScrollPolicy:ScrollPolicy ) : Void
	/**
	* コンストラクタ
	*
	* @param size: 表示領域の指定です。
	* @param hScrollPolicy: 水平方向のスクロールバーのポリシーを設定します。
	* @param vScrollPolicy: 垂直方向のスクロールバーのポリシーを設定します。
	*/
	public function new( size:UXSize, ?hScrollPolicy:ScrollPolicy, ?vScrollPolicy:ScrollPolicy ) : Void
	{
		super();
		this.size = size;
		this.hScrollPolicy = hScrollPolicy;
		this.vScrollPolicy = vScrollPolicy;
		auto = true;
		hScrollBar = new UXScrollBar(new UXSize(size.width,20),  UXScrollBarDirection.HORIZONTAL);
		vScrollBar = new UXScrollBar(new UXSize(20,size.height), UXScrollBarDirection.VERTICAL);
	}

	// }}}
}
// }}}

// {{{ class UXScrollArea extends UXComponent<UXScrollAreaState>
/**
 * UXScrollAreaクラス<br>
 * スクロール可能な領域を実現するコンポーネントクラスです。縦方向、横方向それぞれのスクロールバーを
 * 常に表示する、常に表示しない、必要な時のみ表示するのいずれかを選んで設定することができます。<br>
 *
 * @author		iZE Co., Ltd.<br>
 * @version		$Id$<br>
 */
class UXScrollArea extends UXComponent<UXScrollAreaState>
{
	// {{{ properties

	public var view:UXBasicComponent;
	public var base:UXBasicComponent;
	public var masq:UXBasicComponent;
	public var frame_count:Int;
	public var baseWidth:Float;
	public var baseHeight:Float;

	public static var scrollable:UXScrollArea;

	// }}}
	// {{{ public function new( size:UXSize, ?maskSp:UXComponentResource, ?hScrollPolicy : ScrollPolicy, ?vScrollPolicy : ScrollPolicy )
	/**
	* コンストラクターです。
	* 引数の横方向、縦方向のスクロールバーポリシーを受け取ってスクロールエリアインスタンスを返します。
	* デフォルト値は両方ともにAUTOです。
	*
	* @param size: 全体のサイズを指定します。
	* @param maskSp: マスク用のリソースを指定します。表示領域の設定に該当します。
	* @param hScrollPolicy: 水平方向のスクロールバーのポリシーを設定します。
	* @param vScrollPolicy: 垂直方向のスクロールバーのポリシーを設定します。
	 */
	public function new( size:UXSize, ?maskSp:UXComponentResource, ?hScrollPolicy : ScrollPolicy, ?vScrollPolicy : ScrollPolicy )
	{
		if (hScrollPolicy == null) hScrollPolicy = AUTO;
		if (vScrollPolicy == null) vScrollPolicy = AUTO;
		state = new UXScrollAreaState(size,hScrollPolicy,vScrollPolicy);
		state.target = this;
		ctrl = new UXController<UXScrollAreaState>( state );
		super(ctrl);
		view = new UXBasicComponent();
		base = new UXBasicComponent();
		//graphics.clear();
		//graphics.lineStyle( 0x000000 );
		//graphics.drawRect( -1, -1, size.width+2, size.height+2 );
		super.addUXChild(view);
		super.addUXChild(state.hScrollBar);
		super.addUXChild(state.vScrollBar);
		view.addUXChild(base);
		if ( maskSp==null ) {
			maskSp = new UXComponentResource();
		}
		this.masq = maskSp;
		view.addUXChild(masq);
		view.mask = masq;
		view.addUXEventListener( UXMouseEvent.WHEEL, onMouseWheel );
		view.addUXEventListener( UXMouseEvent.OVER, onMouseOver );
		view.addUXEventListener( UXMouseEvent.OUT,  onMouseOut );

		setSize( state.size );

		var me = this;
		state.hScrollBar.ctrl.getInteraction("Scrolled").addAct( function(s) {
			var vw = me.baseWidth - me.state.size.width;
			var val = -s.getScrollValue();
			me.base.x = vw * val;
		});
		state.vScrollBar.ctrl.getInteraction("Scrolled").addAct( function(s) {
			var vh = me.baseHeight - me.state.size.height;
			var val = -s.getScrollValue();
			me.base.y = vh * val;
		});
		frame_count = 0;
		baseWidth  = 0.0;
		baseHeight = 0.0;
		updateBaseSize();
	}
	// }}}
	// {{{ public function onMouseWheel( e:Dynamic ) : Void

	public function onMouseWheel( e:Dynamic ) : Void
	{
		if ( scrollable==null || scrollable==this ) {
			state.vScrollBar.state.mouse = e;
			state.vScrollBar.ctrl.trigger("Wheel");
		}
	}

	// }}}
	// {{{ public function onMouseOver( e:Dynamic ) : Void

	public function onMouseOver( e:Dynamic ) : Void
	{
		scrollable = this;
		e.preventDefault();
		e.stopImmediatePropagation();
	}

	// }}}
	// {{{ public function onMouseOut( e:Dynamic ) : Void

	public function onMouseOut( e:Dynamic ) : Void
	{
		scrollable = null;
	}

	// }}}
	// {{{ public function addUXChild( child : UXObject ) : Void
	/**
	 * 子要素を加えます。
	 */
	public override function addUXChild( child : UXObject ) : Void
	{
		/* with flash.
		 * 子要素のrenderイベントを取得して、自分自身の範囲と子要素の範囲を考慮して、
		 * 必要であればスクロールバーを更新します。
		 */
		base.addUXChild( child );
		frame_count = 0;
		baseWidth  = 0.0;
		baseHeight = 0.0;
		addUXEventListener( Event.ENTER_FRAME, onEnterFrame );
	}
	// }}}
	// {{{ public function removeUXChild( child : UXObject ) : Void
	/**
	 * 指定された子要素をコンテナから削除します。
	 */
	public override function removeUXChild( child : UXObject ) : Void
	{
		base.removeUXChild( child );
		frame_count = 0;
		baseWidth  = 0.0;
		baseHeight = 0.0;
		addUXEventListener( Event.ENTER_FRAME, onEnterFrame );
	}
	// }}}
	// {{{ public function onEnterFrame( e:Dynamic ) : Void

	public function onEnterFrame( e:Dynamic ) : Void
	{
		if ( frame_count>3 ) {
			getMaxBaseSize(base,0,0);
			removeUXEventListener( Event.ENTER_FRAME, onEnterFrame );
			frame_count = 0;
			updateBaseSize();
		}
		frame_count++;
	}

	// }}}
	// {{{ public function getMaxBaseSize( p:DisplayObjectContainer, x:Float, y:Float ) : Void

	public function getMaxBaseSize( p:DisplayObjectContainer, x:Float, y:Float ) : Void
	{
		for ( i in 0...p.numChildren ) {
			var c:Dynamic = p.getChildAt(i);
			if ( x+c.x+c.width  > baseWidth )  baseWidth  = x+c.x+c.width;
			if ( y+c.y+c.height > baseHeight ) baseHeight = y+c.y+c.height;
			if ( Std.is(c,DisplayObjectContainer) ) {
				getMaxBaseSize(c,x+c.x,y+c.y);
			}
		}
	}

	// }}}
	// {{{ public function updateBaseSize() : Void

	public function updateBaseSize() : Void
	{
		base.graphics.clear();
		base.graphics.lineStyle( 1.0, 0x000000, 0.0 );
		base.graphics.beginFill( 0x000000, 0.0 );
		var w = baseWidth  > view.width  ? baseWidth  : view.width;
		var h = baseHeight > view.height ? baseHeight : view.height;
		base.graphics.drawRect( 0, 0, w, h );
		state.hScrollBar.state.setMaxScroll( baseWidth/state.size.width );
		state.vScrollBar.state.setMaxScroll( baseHeight/state.size.height );
		var hsb_state = state.hScrollBar.state;
		var vsb_state = state.vScrollBar.state;
		// {{{ enable/disable
		// {{{ hScrollBar enable/disable
		if ( baseWidth<=state.size.width ) {
			// show all components
			Act.exec( hsb_state.acts.disable(), hsb_state );
		} else {
			// too big to show
			Act.exec( hsb_state.acts.enable(), hsb_state );
		}
		// }}}
		// {{{ vScrollBar enable/disable
		if ( baseHeight<=state.size.height ) {
			// show all components
			Act.exec( vsb_state.acts.disable(), vsb_state );
		} else {
			// too big to show
			Act.exec( vsb_state.acts.enable(), vsb_state );
		}
		// }}}
		// }}}
		switch ( state.hScrollPolicy ) {
			// {{{ show/hide
			// {{{ case AUTO:
			case AUTO:
				if ( baseWidth<=state.size.width ) {
					// show all components
					state.hScrollBar.visible = false;
				} else {
					// too big to show
					state.hScrollBar.visible = true;
				}
			// }}}
			// {{{ case ON:
			case ON:
				state.hScrollBar.visible = true;
			// }}}
			// {{{ case OFF:
			case OFF:
				state.hScrollBar.visible = false;
			// }}}
			// }}}
		}
		switch ( state.vScrollPolicy ) {
			// {{{ show/hide
			// {{{ case AUTO:
			case AUTO:
				if ( baseHeight<=state.size.height ) {
					// show all components
					state.vScrollBar.visible = false;
				} else {
					// too big to show
					state.vScrollBar.visible = true;
				}
			// }}}
			// {{{ case ON:
			case ON:
				state.vScrollBar.visible = true;
			// }}}
			// {{{ case OFF:
			case OFF:
				state.vScrollBar.visible = false;
			// }}}
			// }}}
		}
	}

	// }}}
	// {{{ public function setSize( size:UXSize ) : Void
	/**
	 * サイズ変更をおこないます。
	 */
	public function setSize( size:UXSize ) : Void
	{
		//graphics.clear();
		//graphics.lineStyle( 0x000000 );
		//graphics.drawRect( -1, -1, size.width+2, size.height+2 );
		state.size = size;
		state.hScrollBar.y = state.size.height;
		state.vScrollBar.x = state.size.width;
		masq.graphics.clear();
		masq.graphics.beginFill(0x000000);
		masq.graphics.drawRect(0,0,state.size.width,state.size.height);
		masq.graphics.endFill();
		state.hScrollBar.state.size = new UXSize(state.size.width,20);
		state.vScrollBar.state.size = new UXSize(20,state.size.height);
		updateBaseSize();
		state.hScrollBar.state.refresh();
		state.vScrollBar.state.refresh();
	}
	// }}}
	// {{{ public function ensure() : Void
	/**
	 * 子要素の位置を見てスクロールバーの設定を行います。
	 */
	public function ensure() : Void
	{
	}
	// }}}
	// {{{ public function update( updator : UXScrollAreaState -> Void ) : Void
	/**
	 * このコンポーネントの状態を直接変更するメソッドです。状態のどんな操作も可能になるので、
	 * このメソッドの使用には注意が必要です。
	 */
	public function update( updator : UXScrollAreaState -> Void ) : Void
	{
		updator( state );
	}
	// }}}
}
// }}}
